<?php
require_once 'Sabai/Event/Dispatcher.php';
require_once 'Sabai/Handle/Decorator/Cache.php';
require_once 'Sabai/Handle/Decorator/Autoload.php';
require_once 'Sabai/Handle/Class.php';
require dirname(__FILE__) . '/Plugin.php';

class Xigg_PluginManager extends Sabai_Event_Dispatcher
{
    var $_id;
    var $_locator;
    var $_pluginDir;
    var $_pluginPrefix;

    function Xigg_PluginManager($id, &$locator, $pluginDir, $pluginPrefix)
    {
        $this->_id = $id;
        $this->_locator =& $locator;
        $this->_pluginDir = (array)$pluginDir;
        $this->_pluginPrefix = $pluginPrefix;
    }

    function getPluginDir()
    {
        return $this->_pluginDir;
    }

    function getLocalPlugins($force = false)
    {
        $cacher =& $this->_locator->getService('Cacher');
        if ($force || (!$cached = $cacher->get('plugins_data', $this->_id, 'Xigg_PluginManager'))) {
            require_once dirname(__FILE__) . '/PluginInfo.php';
            $data = array();
            foreach ($this->_pluginDir as $_plugin_dir) {
                if ($dh = opendir($_plugin_dir)) {
                    while (false !== $file = readdir($dh)) {
                        if (preg_match('/^[a-zA-Z]+[a-zA-Z0-9]*[a-zA-Z0-9]+$/i', $file) && empty($data[$file])) {
                            $plugin_dir = $_plugin_dir . '/' . $file;
                            if (is_dir($plugin_dir)) {
                                $plugin_file_info = $plugin_dir . '/Info.php';
                                $plugin_file_main = $plugin_dir . '/Main.php';
                                if (file_exists($plugin_file_info) && file_exists($plugin_file_main)) {
                                    require_once $plugin_file_info;
                                    require_once $plugin_file_main;
                                    $plugin_class_info = $this->_pluginPrefix . $file . '_Info';
                                    $plugin_class_main = $this->_pluginPrefix . $file . '_Main';
                                    if (class_exists($plugin_class_info) && class_exists($plugin_class_main)) {
                                        $plugin_info =& new $plugin_class_info($file, $plugin_dir);
                                        $plugin_events = array_map(array(&$this, '_mapEvent'), array_filter(get_class_methods($plugin_class_main), array(&$this, '_filterEvent')));
                                        $data[$file] = array(
                                                        'dir'           => $plugin_dir,
                                                        'file'          => $plugin_file_main,
                                                        'class'         => $plugin_class_main,
                                                        'params'        => $plugin_info->getParams(),
                                                        'version'       => $plugin_info->getVersion(),
                                                        'summary'       => $plugin_info->getSummary(),
                                                        'description'   => $plugin_info->getDescription(),
                                                        'can_uninstall' => $plugin_info->canUninstall(),
                                                        'events'        => $plugin_events,
                                                        'dependencies'  => $plugin_info->getDependencies(),
                                                        );
                                    } else {
                                        trigger_error(sprintf('Required class %s and/or %s does not exist for plugin %s', $plugin_class_info, $plugin_class_main, $file), E_USER_WARNING);
                                    }
                                } else {
                                    trigger_error(sprintf('Required class file does not exist for plugin %s', $file), E_USER_WARNING);
                                }
                            }
                        }
                    }
                    closedir($dh);
                }
                $cacher->save(serialize($data), 'plugins_data', $this->_id, 'Xigg_PluginManager');
            }
        } else {
            $data = unserialize($cached);
        }
        return $data;
    }

    function getLocalPlugin($pluginName, $force = false)
    {
        $local_plugins = $this->getLocalPlugins($force);
        if (isset($local_plugins[$pluginName])) {
            return $local_plugins[$pluginName];
        }
        return false;
    }

    function &getInstalledPlugins($active = false)
    {
        $model =& $this->_locator->getService('Model');
        $plugin_r =& $model->getRepository('Plugin');
        if ($active) {
            $plugins =& $plugin_r->fetchByCriteria(Sabai_Model_Criteria::createValue('plugin_active', 1));
        } else {
            $plugins =& $plugin_r->fetchAll();
        }
        return $plugins;
    }

    function &isPluginInstalled($pluginName)
    {
        $plugin = false;
        $model =& $this->_locator->getService('Model');
        $plugin_r =& $model->getRepository('Plugin');
        $plugins =& $plugin_r->fetchByCriteria(Sabai_Model_Criteria::createValue('plugin_name', $pluginName));
        if ($plugins->size() > 0) {
            $plugin =& $plugins->getFirst();
        }
        return $plugin;
    }

    function loadPlugins($force = false)
    {
        $cacher =& $this->_locator->getService('Cacher');
        if ($force || (!$cached = $cacher->get('plugins', $this->_id, 'Xigg_PluginManager'))) {
            $this->clearEventListners();
            $local = $this->getLocalPlugins($force);
            $installed =& $this->getInstalledPlugins(true);
            while ($plugin =& $installed->getNext()) {
                $plugin_name = $plugin->getLabel();
                if ($plugin_data = @$local[$plugin_name]) {
                    $plugin_params = $plugin->getParams();
                    foreach ($plugin_data['params'] as $param_name => $param_data) {
                        if (!array_key_exists($param_name, $plugin_params)) {
                            $plugin_params[$param_name] = $param_data['default'];
                        }
                    }
                    $listner_handle =& new Sabai_Handle_Decorator_Cache(
                                           new Sabai_Handle_Decorator_Autoload(
                                               new Sabai_Handle_Class($plugin_data['class'], array($plugin_name, $plugin_data['dir'], $plugin_params)),
                                                   $plugin_data['file']));
                    $this->addEventListner($plugin_data['events'], $listner_handle, $plugin_name);
                }
            }
            $cacher->save(serialize($this->getEventListners()), 'plugins', $this->_id, 'Xigg_PluginManager');
        } else {
            $this->setEventListners(unserialize($cached));
        }
    }

    function &_instantiateListner(&$listnerHandle)
    {
        $listner =& parent::_instantiateListner($listnerHandle);
        $listner->setDB($this->_locator->getService('DB'));
        $listner->setCacher($this->_locator->getService('Cacher'));
        return $listner;
    }

    function _filterEvent($method)
    {
        return (strpos($method, 'on') === 0);
    }

    function _mapEvent($method)
    {
        return substr($method, 2);
    }
}